"""

    File: encoder_py.py
    Description: 
    
        This module encodes AMF packets into a raw byte stream.  

    Author: Kyle Vogt
    Date  : October 8th, 2007
    Copyright (c) 2007, Justin.tv, Inc.
    
"""

import struct, traceback, re
from twisted.python import log
from pms.rtmp import amf

class Channel():
    
    def __init__(self, id):
        self.id = id
        self.stream_id = None
        self.timecode = None
        self.absflag = None
        self.length = None
        self.type = None

class Encoder():
    
    def __init__(self):
        self.parser = amf.Amf()
        self.data = ''
        self.channels = {}
        self.setChunkSize(128)
        
    def process(self, data, channel_id, stream_id, type, timecode=0, absflag=False):
        "Encode the data into the given stream"
        id =  int(channel_id)
        channel = self.channels.get(id)
        # If we don't have this channel yet, create it
        if channel is None:
            channel = {
                'id' : id,
                'stream_id' : -1,
                'length' : -1,
                'timecode' : -1,
                'absflag' : -1,
                'type' : -1}
            self.channels[id] = channel
        # If we are encoding AMF data, do it here
        if isinstance(data, list):
            data = self.parser.encode(data)
        # New stream_id?
        if stream_id != channel['stream_id'] or absflag:
            header_length = 12
        elif len(data) != channel['length']:
            header_length = 8
        elif timecode != channel['timecode']:
            header_length = 4
        else:
            header_length = 1
        # Update channel data
        length = len(data)
        channel['stream_id'] = stream_id
        channel['length'] = length
        channel['timecode'] = timecode
        channel['absflag'] = absflag
        channel['type'] = type
        # Generate header
        if header_length > 1:
            timecode = struct.pack("!I", int(timecode))[1:]
        if header_length > 4:
            length = struct.pack("!I", length)[1:]
        if header_length == 12:
            header = struct.pack('B3s3sBI', id, timecode, length, type, stream_id)
        elif header_length == 8:
            header = struct.pack('B3s3sB', 0x40 + id, timecode, length, type)
        elif header_length == 4:
            header = struct.pack('B3s', 0x80 + id, timecode)
        else:
            header = chr(0xC0 + id)
        # Do we need to bother with the chunkifier?
        if len(data) <= self.chunkSize:
            return ''.join([header, data])
        else:
            return self.chunkify2(header, channel_id, data)

    def setChunkSize(self, size):
        "Update the chunk size and rebuild the regular expression"
        self.chunkSize = size
        self.chunker = re.compile(r'(.{%s})' % self.chunkSize, re.S)

    def chunkify2(self, header, channel_id, data):
        "Split the data into chunks of chunkSize bytes"
        chunks = self.chunker.findall(data)
        left = (len(data) % self.chunkSize)
        if left: chunks.append(data[-left:])
        return ''.join([header, chr(0xC0 + channel_id).join(chunks)])    

